package com.vpen.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;

/**
 * @author Vpen
 */
@Slf4j
public class GenerateCertificate {

    private static String rootCertVersion1 = "860106576952879101192782278876319243486072481962999610484027161162448933268423045647258145695082284265933019120714643752088997312766689988016808929265129401027490891810902278465065056686129972085119605237470899952751915070244375173428976413406363879128531449407795115913715863867259163957682164040613505040314747660800424242248055421184038777878268502955477482203711835548014501087778959157112423823275878824729132393281517778742463067583320091009916141454657614089600126948087954465055321987012989937065785013284988096504657892738536613208311013047138019418152103262155848541574327484510025594166239784429845180875774012229784878903603491426732347994359380330103328705981064044872334790365894924494923595382470094461546336020961505275530597716457288511366082299255537762891238136381924520749228412559219346777184174219999640906007205260040707839706131662149325151230558316068068139406816080119906833578907759960298749494098180107991752250725928647349597506532778539709852254478061194098069801549845163358315116260915270480057699929968468068015735162890213859113563672040630687357054902747438421559817252127187138838514773245413540030800888215961904267348727206110582505606182944023582459006406137831940959195566364811905585377246353";
    private static String rootCertVersion2 = "24156627931985958051017183040835577271803742470193804806479316178045088981962804168393398987646446251087541768081971475544151551235525470790716604369379805327668466429966167642117961353233058515180243264560201783520956161510523416923017697354365782825500659342029196527776056976223174394946371372849906309277537461992299774200098515526818746947230488275456663264920440977381968978227273889068919338259949793686590492904029279861913225794809675826299753284990778166519152326723946780528965868736869495336993456735232755342913885746267768375682771655854436236934171901662660193080235109535758464079136573948168636773471";
    private static String commonArgsStr = "EQUAL,65537,24773058818499217187577663886010908531303294206336895556072197892590450942803807164562754911175164262596715237551312004078542654996496301487027034803410086499747369353221485073240039340641397198525027728751956658900801359887190562885573922317930300068615009483578963467556425525328780085523172495307229112069939166202511721671904748968934606589702999279663332403655662225374084460291376706916679151764149324177444374590606643838366605181996272409014933080082205048098737253668016260658830645459388519595314928290853199112791333551144805347785109465401055719331231478162870216035573012645710763533896540021550083104281->3,24773058818499217187577663886010908531303294206336895556072197892590450942803807164562754911175164262596715237551312004078542654996496301487027034803410086499747369353221485073240039340641397198525027728751956658900801359887190562885573922317930300068615009483578963467556425525328780085523172495307229112069939166202511721671904748968934606589702999279663332403655662225374084460291376706916679151764149324177444374590606643838366605181996272409014933080082205048098737253668016260658830645459388519595314928290853199112791333551144805347785109465401055719331231478162870216035573012645710763533896540021550083104281";
    private static String licenseId = "FV8EM46DQYC5AW9";
    private static String licensePart = "{\"licenseId\":\"FV8EM46DQYC5AW9\",\"licenseeName\":\"Vpen\",\"licenseeType\":\"PERSONAL\",\"assigneeName\":\"\",\"assigneeEmail\":\"\",\"licenseRestriction\":\"\",\"checkConcurrentUse\":false,\"products\":[{\"code\":\"GO\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RS0\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"DM\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"CL\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RSU\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RSC\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"PC\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"DS\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RD\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RC\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RSF\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"RM\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"II\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"DPN\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"DB\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"DC\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"PS\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"RSV\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"WS\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":false},{\"code\":\"PSI\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"PCWMP\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"RS\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"DP\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"PDB\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true},{\"code\":\"PRR\",\"fallbackDate\":\"2026-09-14\",\"paidUpTo\":\"2026-09-14\",\"extended\":true}],\"metadata\":\"0220240702PSAX000005X\",\"hash\":\"12345678/0-541816629\",\"gracePeriodDays\":7,\"autoProlongated\":false,\"isAutoProlongated\":false,\"trial\":false,\"aiAllowed\":true}";


    public static void main(String[] args) throws Exception {

        // 1.生成证书

        Security.addProvider(new BouncyCastleProvider());

        // 设置日期
        Date today = new Date();
        Date yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000L);
        Date tenYearsLater = new Date(today.getTime() + 3650L * 24 * 60 * 60 * 1000);

        // 生成 RSA 密钥对
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(4096);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 构建证书
        X500Name issuer = new X500Name("CN=JetProfile CA");
        X500Name subject = new X500Name("CN=MoYuno-from-2022-07-25");

        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                issuer,
                BigInteger.valueOf(System.currentTimeMillis()),
                yesterday,
                tenYearsLater,
                subject,
                keyPair.getPublic()
        );

        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build(keyPair.getPrivate());
        X509CertificateHolder certHolder = certBuilder.build(contentSigner);

        X509Certificate certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);

        write2File(keyPair, certificate);


        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

        // 获取模长
        int modBits = ((RSAPublicKey) publicKey).getModulus().bitLength();

        MessageDigest digestCert = MessageDigest.getInstance("SHA-256");
        digestCert.update(certificate.getTBSCertificate());

        byte[] digest1 = digestCert.digest();

        log.info("pacs after :{}", Base64.getEncoder().encodeToString(digest1));
        // 使用 PKCS#1.5 编码
        String res = new BigInteger(1, pkcs15Encode(digest1, (modBits + 7) / 8, true)).toString();
        log.info("pacs 1.5 :{}", res);

        // 定义输出字符串
        StringBuilder power = new StringBuilder("[Result]\n; Suit 230914\n");
        power.append("EQUAL,").append(str(certificate.getSignature())).append(",65537,").append(rootCertVersion1).append("->").append(res).append("\n");
        power.append("EQUAL,").append(str(certificate.getSignature())).append(",65537,").append(rootCertVersion2).append("->").append(res).append("\n\n");
        power.append("[Args]\n").append(commonArgsStr).append("\n");


        // 写入 power.conf 文件
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("power.conf"))) {
            writer.write(power.toString());
        }
        System.out.println("Write into power.conf");


        // 创建Signature对象并初始化
        Signature signature = Signature.getInstance("SHA1withRSA");
        signature.initSign(privateKey);

        // 更新要签名的数据
        signature.update(licensePart.getBytes(StandardCharsets.UTF_8));

        // 生成签名
        byte[] sign = signature.sign();


        String encodedSignature = Base64.getEncoder().encodeToString(sign);
        String licensePartBase64 = Base64.getEncoder().encodeToString(licensePart.getBytes(StandardCharsets.UTF_8));

        String string = FileUtil.readUtf8String(new File("ca.crt"));
        String certStr = StrUtil.subBetween(string,"-----BEGIN CERTIFICATE-----","-----END CERTIFICATE-----").replaceAll("\\s+", "");

        String result = licenseId + "-" + licensePartBase64 + "-" + encodedSignature + "-" + certStr;
        System.out.println("------");
        System.out.println(result);
    }


    @SneakyThrows
    public static void write2File(KeyPair keyPair, X509Certificate certificate) {

        // 将私钥写入文件
        try (JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(new FileOutputStream("ca.key")))) {
            try {
                writer.writeObject(keyPair.getPrivate());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try (JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(new FileOutputStream("ca.pub")))) {
            try {
                writer.writeObject(keyPair.getPublic());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 将证书写入文件
        try (JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(new FileOutputStream("ca.crt")))) {
            try {
                writer.writeObject(certificate);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }



    /**
     * 验证签名
     *
     * @param publicKey:
     * @param digest:
     * @param signature:
     * @author Vpen
     * @date 2024/11/16 12:52
     */
    public static void verifySignature(PublicKey publicKey, byte[] digest, byte[] signature) throws SignatureException {
        try {
            Signature verifier = Signature.getInstance("SHA1withRSA");
            verifier.initVerify(publicKey);
            verifier.update(digest);
            if (!verifier.verify(signature)) {
                throw new SignatureException("Signature verification failed");
            }
            System.out.println("1111");
        } catch (Exception e) {
            throw new SignatureException("Signature verification failed", e);
        }
    }



    public static byte[] pkcs15Encode(byte[] msgHash, int emLen, boolean withHashParameters) throws Exception {
        // Define the OID for the hash algorithm used (SHA-256)
        ASN1ObjectIdentifier sha256Oid = new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.1"); // SHA-256 OID

        ASN1EncodableVector digestAlgo = new ASN1EncodableVector();
        digestAlgo.add(sha256Oid);

        if (withHashParameters) {
            digestAlgo.add(DERNull.INSTANCE);
        }

        // Create the DigestInfo structure
        DEROctetString digest = new DEROctetString(msgHash);
        ASN1EncodableVector encodableVector = new ASN1EncodableVector();
        encodableVector.add(new DERSequence(digestAlgo));
        encodableVector.add(digest);
        DERSequence digestInfo = new DERSequence(encodableVector);

        byte[] encodedDigestInfo = digestInfo.getEncoded(ASN1Encoding.DER);

        // Check if the output length is sufficient
        if (emLen < encodedDigestInfo.length + 11) {
            throw new IllegalArgumentException("Selected hash algorithm has a too long digest.");
        }

        // Create the padding
        byte[] ps = new byte[emLen - encodedDigestInfo.length - 3];
        Arrays.fill(ps, (byte) 0xFF);

        // Construct the final encoded message
        byte[] result = new byte[emLen];
        result[0] = 0x00;
        result[1] = 0x01;
        System.arraycopy(ps, 0, result, 2, ps.length);
        result[ps.length + 2] = 0x00;
        System.arraycopy(encodedDigestInfo, 0, result, ps.length + 3, encodedDigestInfo.length);

        // Print the results
        System.out.println("res:" + Base64.getEncoder().encodeToString(result));
        System.out.println("md:" + Base64.getEncoder().encodeToString(msgHash));

        return result;
    }



    public static String str(byte[] signature) {
        // 将字节数组转换为整数（大端字节顺序）
        BigInteger number = new BigInteger(1, signature); // 1 表示数字是正数

        return String.valueOf(number.toString());
    }


}
